
// 全局变量
const g = require('../utils/global');
const { CellModel } = require('cellModel');
class GameModel {
    constructor() {
        this.type = null;
        // 格子
        this.cells = null;
        this.lastPos = cc.v2(-1, -1);
        this.cellTypeNum = 5;
        this.cellCreateType = [];
        // 分数
        this.score = 0;
    }

    // 初始化
    init(cellTypeNum) {
        this.cells = [];
        this.setCellTypeNum(cellTypeNum || this.cellTypeNum);
        // 从左上角开始
        for (let i = 1; i <= g.GRID_ROWS; ++i) {
            this.cells[i] = [];
            for (let j = 1; j <= g.GRID_COLUMNS; ++j) {
                this.cells[i][j] = new CellModel();
            }
        }

        for (let i = 1; i <= g.GRID_ROWS; ++i) {
            for (let j = 1; j <= g.GRID_COLUMNS; ++j) {
                let flag = true;
                while(flag) {
                    flag = false;
                    this.cells[i][j].init(this.getRandomCellType());
                    let result = this.checkPoint(i, j)[0];
                    if (result.length > 2) {
                        flag = true;
                    }
                    this.cells[i][j].setXY(i, j);
                    this.cells[i][j].setStartXY(i,j);
                }
            }
        }
    }

    printCells(msg) {
        cc.log(`================= ${msg} =================`);
        for(let i = 1; i <= g.GRID_ROWS; ++i) {
            const s = [];
            const status = [];
            // 坐标
            const pos = [];
            for(let j = 1; j <=g.GRID_COLUMNS; ++j) {
                if (this.cells[i][j]) {
                    s.push(this.cells[i][j].type);
                    status.push(this.cells[i][j].status);
                    pos.push(`(${this.cells[i][j].x}, ${this.cells[i][j].y})`);
                } else {
                    s.push('=');
                    status.push('=')
                }
            }
            cc.log(`${s.join(',')} | ${status.join(',')} | ${pos.join(',')}`);
        }
    }

    // 检查是否有3个相同的元素
    checkPoint(x, y) {
        const checkWithDirection = function (x, y, direction) {
            const queue = [];
            const visited = [];
            // 已经遍历的格子
            visited[x*g.GRID_ROWS+y] = true;
            queue.push(cc.v2(x,y));
            let next = 0;
            while (next < queue.length) {
                const point = queue[next];
                next++;
                const cellModel = this.cells[point.x][point.y];
                if(!cellModel) {
                    // 当前没有格子
                    continue;
                }
                for(let i = 0; i <direction.length; ++i) {
                    const tmpX = point.x+direction[i].x;
                    const tmpY = point.y+direction[i].y;
                    // 检查边界
                    if(tmpX < 1 || tmpX > g.GRID_ROWS
                        || tmpY < 1 || tmpY > g.GRID_COLUMNS
                        || visited[tmpX*g.GRID_ROWS+tmpY]
                        || !this.cells[tmpX][tmpY]) {
                        continue;
                    }
                    if(cellModel.type === this.cells[tmpX][tmpY].type) {
                        visited[tmpX*g.GRID_ROWS+tmpY] = true;
                        queue.push(cc.v2(tmpX, tmpY));
                    }
                }
            }
            return queue;
        }.bind(this);
        // 检查行
        const rowResult = checkWithDirection(x, y, [cc.v2(0, 1), cc.v2(0, -1)]);
        // 检查列
        const colResult = checkWithDirection(x, y, [cc.v2(1, 0), cc.v2(-1, 0)]);
        let result = [];
        let newCellStatus = '';
        if (rowResult.length >= 5 || colResult.length >= 5) {
            newCellStatus = g.CELL_STATUS.bird;
        } else if (rowResult.length >= 3 && colResult.length >= 3) {
            newCellStatus = g.CELL_STATUS.wrap;
        } else if (rowResult.length >= 4) {
            newCellStatus = g.CELL_STATUS.line;
        } else if (colResult.length >= 4) {
            newCellStatus = g.CELL_STATUS.column;
        }
        // 合并结果
        let score = 0;
        if (rowResult.length >= 3) {
            result = rowResult;
            score += result.length;
        }
        if (colResult.length >= 3) {
            const dup = result.concat();
            colResult.forEach(newElem => {
                let isAdd = false;
                dup.forEach(oldElem => {
                    if (oldElem.x === newElem.x && oldElem.y === newElem.y) {
                        isAdd = true;
                    }
                }, this);
                if(!isAdd) {
                    score++;
                    result.push(newElem);
                }
            }, this);
        }
        return [result, newCellStatus, this.cells[x][y].type, score];
    }

    selectCell(pos) {
        this.changeModels = [];
        // 动物消失，爆炸等效果
        this.effectQueue = [];
        // 上次位置
        const lastPos = this.lastPos;
        const delta = Math.abs(pos.x - lastPos.x) + Math.abs(pos.y - lastPos.y);
        if (delta !== 1) {
            this.lastPos = pos;
            return [[], []];
        }
        cc.log(`need change in last pos = ${this.lastPos}, next pos = ${pos}`);
        // 当前点击的格子
        let curClickCell = this.cells[pos.x][pos.y];
        // 上一次点击的格子
        let lastClickCell = this.cells[lastPos.x][lastPos.y];
        this.exchangeCell(lastPos, pos);
        const result1 = this.checkPoint(pos.x, pos.y)[0];
        const result2 = this.checkPoint(lastPos.x, lastPos.y)[0];
        this.curTime = 0; // 动画播放的当前时间
        this.saveChangeModel(curClickCell);
        this.saveChangeModel(lastClickCell);
        // 判断两个是否是特殊的动物
        const isCanBomb = (curClickCell.status !== g.CELL_STATUS.common &&
            lastClickCell.status !== g.CELL_STATUS.common)
            || curClickCell.status === g.CELL_STATUS.bird
            || lastClickCell.status === g.CELL_STATUS.bird;
        // 不会发生消除的情况
        if (result1.length < 3 && result2.length < 3 && !isCanBomb) {
            this.exchangeCell(lastPos, pos);
            curClickCell.moveToAndBack(lastPos);
            lastClickCell.moveToAndBack(pos);
            // 重新选择
            this.lastPos = cc.v2(-1, -1);
            return [this.changeModels]
        } else {
            this.lastPos = cc.v2(-1, -1);
            curClickCell.moveTo(lastPos, this.curTime);
            lastClickCell.moveTo(pos, this.curTime);
            const checkPoint = [pos, lastPos];
            this.curTime += g.ANI_TIME.move;
            this.processCrush(checkPoint);
            return [this.changeModels, this.effectQueue];
        }
    }

    /**
     *
     * @param checkPoint 需要检查的格子
     */
    // 消除
    processCrush(checkPoint) {
        // 第几轮消除
        let cycleCount = 0;
        while (checkPoint.length > 0 ){
            // 需要爆炸效果
            let bombModels = [];
            if (cycleCount === 0 && checkPoint.length === 2) {
                // 特殊消除 bird
                const pos1 = checkPoint[0];
                const pos2 = checkPoint[1];
                const model1 = this.cells[pos1.x][pos1.y];
                const model2 = this.cells[pos2.x][pos2.y];
                if (model1.status === g.CELL_STATUS.bird || model2.status === g.CELL_STATUS.bird) {
                    if (model1.status === g.CELL_STATUS.bird) {
                        model1.type = model2.type;
                        bombModels.push(model1);
                    } else {
                        model2.type = model1.type;
                        bombModels.push(model2);
                    }
                }

            }
            for(const pos of checkPoint) {
                if (!this.cells[pos.x][pos.y]) {
                    continue;
                }
                const [ result, newCellStatus, newCellType, score ] = this.checkPoint(pos.x, pos.y);
                if (result.length < 3) {
                    continue;
                }
                for(const res of result) {
                    const model = this.cells[res.x][res.y];
                    // 普通爆炸效果
                    this.crushCell(res.x, res.y, false, cycleCount);
                    if (model.status !== g.CELL_STATUS.common) {
                        bombModels.push(model);
                    }
                }
                // 生成特殊格子
                this.createNewCell(pos, newCellStatus, newCellType);
                // add score
                this.score += score;
                cc.log(`score = ${this.score}, cycle count = ${cycleCount}`);
            }
            this.processBomb(bombModels, cycleCount);
            // 暂停动画播放时间
            this.curTime += g.ANI_TIME.die;
            checkPoint = this.down();
            cycleCount++;
        }
    }

    // 下落格子
    down() {
        this.printCells('before down');
        const newCheckPoint = [];
        // 按列，坐标原点左上角
        for (let j = g.GRID_COLUMNS; j >=1; --j) {
            for (let i = g.GRID_ROWS; i >= 1; --i) {
                if (this.cells[i][j]) {
                    // 非空格
                    continue;
                }
                let curRow = i;
                // 计算动画
                for (let next = curRow; next >= 1; --next) {
                    if(!this.cells[next][j]) {
                        // 空格，不需要移动
                        continue;
                    }
                    newCheckPoint.push(this.cells[next][j]);
                    this.cells[curRow][j] = this.cells[next][j];
                    this.cells[next][j] = null;
                    this.cells[curRow][j].setXY(curRow, j);
                    this.cells[curRow][j].moveTo(cc.v2(curRow, j), this.curTime);
                    this.saveChangeModel(this.cells[curRow][j]);
                    curRow--;
                }
                let count = 1;
                // 剩下的都是空格
                for (let next = curRow; next >= 1; --next) {
                    const model = new CellModel();
                    model.init(this.getRandomCellType());
                    model.setStartXY(-count, j);
                    model.setXY(-count, j);
                    model.moveTo(cc.v2(next, j), this.curTime);
                    count++;
                    this.saveChangeModel(model);
                    newCheckPoint.push(model);
                    this.cells[next][j] = model;
                }
                break;
            }
        }
        this.printCells('after down');
        this.curTime += g.ANI_TIME.move + 0.3;
        return newCheckPoint;
    }

    // 处理爆炸效果
    processBomb(bombModels, cycleCount) {
        while (bombModels.length > 0) {
            const newBombModels = [];
            let bombTime = g.ANI_TIME.bomb_delay;
            for(const model of bombModels) {
                cc.log(`bomb model status = ${model.status}`);
                if (model.status === g.CELL_STATUS.line) {
                    // 横向爆炸
                    for (let i = 1; i <= g.GRID_COLUMNS; ++i) {
                        if (!this.cells[model.x][i]) {
                            // 当前没有格子
                            continue;
                        }
                        if (this.cells[model.x][i].status !== g.CELL_STATUS.common) {
                            // 消除的类型
                            this.cells[model.x][i].setBirdType(model.type);
                            newBombModels.push(this.cells[model.x][i])
                        }
                        this.crushCell(model.x, i, false, cycleCount);
                    }
                    // 添加横向爆炸效果
                    this.addRowBomb(this.curTime, cc.v2(model.x, model.y));
                } else if (model.status === g.CELL_STATUS.column) {
                    // 纵向爆炸
                    for (let i = 1; i <= g.GRID_ROWS; ++i) {
                        if (!this.cells[i][model.y]) {
                            // 当前没有格子
                            continue;
                        }
                        if (this.cells[i][model.y].status !== g.CELL_STATUS.common) {
                            this.cells[i][model.y].setBirdType(model.type);
                            newBombModels.push(this.cells[i][model.y])
                        }
                        this.crushCell(i, model.y, false, cycleCount);
                    }
                    this.addColBomb(this.curTime, cc.v2(model.x, model.y));
                } else if (model.status === g.CELL_STATUS.wrap) {
                    // 消掉周围12个方块
                    const x = model.x;
                    const y = model.y;
                    for (let i = 1; i <= g.GRID_ROWS; ++i) {
                        for (let j = 1; j <= g.GRID_COLUMNS; ++j) {
                            const delta = Math.abs(x-i) + Math.abs(y-j);
                            if (this.cells[i][j]&& delta <= 2) {
                                if (this.cells[i][j].status !== g.CELL_STATUS.common) {
                                    this.cells[i][j].setBirdType(model.type);
                                    newBombModels.push(this.cells[i][j]);
                                }
                                this.crushCell(i, j, false, cycleCount);
                            }
                        }
                    }
                } else if (model.status === g.CELL_STATUS.bird) {
                    let crushType = model.type;
                    if (bombTime < g.ANI_TIME.bomb_bird_delay) {
                        bombTime = g.ANI_TIME.bomb_bird_delay;
                    }
                    // 再次生成bird时候，随机指定图标
                    if (crushType === g.CELL_TYPE.bird) {
                        crushType = model.birdType || this.getRandomCellType();
                    }
                    for (let i = 1; i <= g.GRID_ROWS; ++i) {
                        for (let j = 1; j <= g.GRID_COLUMNS; ++j) {
                            if (this.cells[i][j] && this.cells[i][j].type === crushType) {
                                if (this.cells[i][j].status !== g.CELL_STATUS.common) {
                                    this.cells[i][j].setBirdType(model.type);
                                    newBombModels.push(this.cells[i][j]);
                                }
                                this.crushCell(i, j, true, cycleCount);
                            }
                        }
                    }
                }
            }
            if (bombModels.length > 0) {
                this.curTime += bombTime;
            }
            bombModels = newBombModels;
        }
    }

    // 交换
    exchangeCell(pos1, pos2) {
        const tmpModel = this.cells[pos1.x][pos1.y];
        this.cells[pos1.x][pos1.y] = this.cells[pos2.x][pos2.y];
        this.cells[pos1.x][pos1.y].x = pos1.x;
        this.cells[pos1.x][pos1.y].y = pos1.y;
        this.cells[pos2.x][pos2.y] = tmpModel;
        this.cells[pos2.x][pos2.y].x = pos2.x;
        this.cells[pos2.x][pos2.y].y = pos2.y;
    }

    // 生成新块
    createNewCell(pos, cellStatus, cellType) {
        if (cellStatus === '') {
            // 普通格子，不需要生成，用down() 生成
            return;
        }
        // cc.log(`create new cell pos = ${JSON.stringify(pos)}, cellStatus=${cellStatus}, cellType=${cellType}`);
        if(cellStatus === g.CELL_STATUS.bird) {
            cellType = g.CELL_TYPE.bird;
        }
        const model = new CellModel();
        this.cells[pos.x][pos.y] = model;
        model.init(cellType);
        model.setStartXY(pos.x, pos.y);
        model.setXY(pos.x, pos.y);
        model.setStatus(cellStatus);
        model.setVisible(0, false);
        model.setVisible(this.curTime, true);
        this.changeModels.push(model);
    }

    // helper
    getRandomCellType() {
        const index = Math.floor(Math.random()*this.cellTypeNum);
        return this.cellCreateType[index]
    }

    // 设置种类
    setCellTypeNum(num) {
        this.cellTypeNum = num;
        this.cellCreateType = [];
        for (let i = 1; i <= g.CELL_NUM; ++i) {
            // 从1开始
            this.cellCreateType.push(i);
        }
        for (let i = 0; i < this.cellCreateType.length; ++i) {
            const index = Math.floor(Math.random()*(g.CELL_NUM - i)) + i;
            [this.cellCreateType[i], this.cellCreateType[index]] = [this.cellCreateType[index], this.cellCreateType[i]];
        }
    }

    // 更新有改动的model
    saveChangeModel(m) {
        if (this.changeModels.indexOf(m) !== -1) {
            return;
        }
        this.changeModels.push(m);
    }

    // 消除格子
    crushCell(x, y, needShake, step) {
        const model = this.cells[x][y];
        this.saveChangeModel(model);
        if(needShake) {
            model.toShake(this.curTime);
        }
        const shakeTime = needShake ? g.ANI_TIME.die_shake : 0;
        model.toDie(this.curTime+shakeTime);
        this.addCrushEffect(this.curTime+shakeTime, cc.v2(model.x, model.y), step);
        // 删除不留痕迹
        this.cells[x][y] = null;
    }

    /**
     * 播放特效
     * @param playTime， 开始播放的时间
     * @param pos， cell的位置
     * @param step， 第几次消除，用于播放音效
     */
    addCrushEffect(playTime, pos, step) {
        this.effectQueue.push({
           playTime,
           pos,
           action: g.CELL_EFFECT_ACTION.crush,
           step,
        });
    }

    // 横向爆炸
    addRowBomb(playTime, pos) {
        this.effectQueue.push({
            playTime,
            pos,
            action: g.CELL_EFFECT_ACTION.rowBomb,
        })
    }

    // 纵向爆炸
    addColBomb(playTime, pos) {
        this.effectQueue.push({
            playTime,
            pos,
            action: g.CELL_EFFECT_ACTION.colBomb,
        })
    }
}

module.exports = {
    GameModel,
};
